package cn.hchaojie.boot.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import cn.hchaojie.boot.security.JwtAuthenticationFilter;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
	@Autowired
	private AuthenticationSuccessHandler successHandler;
	
	@Autowired
	private AuthenticationFailureHandler failureHanlder;
	
	@Autowired
	private DataSource dataSource;
	
	@Autowired
	private UserDetailsService userDetailsService;
	
	// 处理jwt token认证的filter
	@Autowired
	private JwtAuthenticationFilter jwtAuthenticationFilter;
	
	/**
	 * 配置登陆页面、请求路径拦截规则
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
		.formLogin()
			// 未登陆时，重定向到这个路径，然后写一个controller处理这个路径的请求，返回一个
			// json错误消息
			.loginPage("/auth/require")
			.loginProcessingUrl("/login")
			.successHandler(successHandler)
			.failureHandler(failureHanlder)
		.and()
			// 使用jwt token机制认证，不需要开启session
			.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
		.and()
			.rememberMe()
//			.alwaysRemember(true)		// 一般情况下让用户选择
			.tokenRepository(configTokenRepository())
			.userDetailsService(userDetailsService)
			.tokenValiditySeconds(7 * 24 * 60 * 60)	  // token过期时间，7天
		.and()
			.authorizeRequests()	// 开始配置请求认证规则
			.antMatchers("/auth/require").permitAll()
			.antMatchers("/auth/register").permitAll()
			.antMatchers("/admin/**").hasRole("admin")		// 需要admin权限才能访问
			.antMatchers("/sales/**").hasRole("sales")		// 需要sales权限才能访问
			.anyRequest()
			.authenticated()		// 所有请求都需要认证
		.and()
			// 请求没有带token、或者token过期
			.exceptionHandling().authenticationEntryPoint(
				(request, response, e) -> response.sendError(HttpStatus.UNAUTHORIZED.value(), e.getMessage())
			)
		.and()
			.csrf().disable();
		
		http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
	}
	
	@Bean
	public BCryptPasswordEncoder configPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	public PersistentTokenRepository configTokenRepository() {
		JdbcTokenRepositoryImpl dao = new JdbcTokenRepositoryImpl();
		dao.setDataSource(dataSource);
		
		return dao;
	}
}
